#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pygame import *

font.init()
from math import cos, radians

try:
    import GetEvent
except:
    from . import GetEvent

MODE = 0


def menu(
        menu,  # iterable of str as ("item",) or ("item::tooltip",)
        font1=None,  # font object|None(pygame default font): unhighlighted item font
        font2=None,  # font object|None(font1): highlighted item font
        color1=(128, 128, 128),  # (int,int,int)|color object: unhighlighted item color
        color2=None,  # (int,int,int)|color object|None(calculated from the light arg): highlighted/neon item color
        interline=5,  # int: items spacing
        justify=True,  # boolean
        light=5,  # int in range [-10,10]: use if color2 is None
        speed=300,  # int (0 = no sliding): anim speed
        lag=30,  # int in range [0,90]
        neon=True,  # boolean: set neon effect
        tooltipfont=None,  # font object|None(pygame default font)
        tooltiptime=2000,  # int
        cursor_img=None,
        hotspot=(0, 0),
        x=None,
        y=None,
        topleft=None,
        midtop=None,
        topright=None,
        midleft=None,
        center=None,
        midright=None,
        bottomleft=None,
        midbottom=None,
        bottomright=None,
        centerx=None,
        centery=None
):
    """
    menu(
         menu,                          # iterable of str as ("item",) or ("item::tooltip",)
         font1      = None,             # font object|None(pygame default font): unhighlighted item font
         font2      = None,             # font object|None(font1): highlighted item font
         color1     = (128,128,128),    # (int,int,int)|color object: unhighlighted item color
         color2     = None,             # (int,int,int)|color object|None(calculated from the light arg): highlighted/neon item color
         interline  = 5,                # int: items spacing
         justify    = True,             # boolean
         light      = 5,                # int in range [-10,10]: use if color2 is None
         speed      = 300,              # int (0 = no sliding): anim speed
         lag        = 30,               # int in range [0,90]
         neon       = True,             # boolean: set neon effect
         tooltipfont= None,             # font object|None(pygame default font)
         tooltiptime= 2000              # int
         cursor_img = None,
         hotspot    = (0,0),
         x          = None,
         y          = None,
         topleft    = None,
         midtop     = None,
         topright   = None,
         midleft    = None,
         center     = None,
         midright   = None,
         bottomleft = None,
         midbottom  = None,
         bottomright= None,
         centerx    = None,
         centery    = None

    return: (None,None) if hit escape else (item,index)

    """
    global hold_bg_cursor
    shad = 100  # test

    class Item(Rect, object):
        def __init__(self, rect, label, tooltip):
            Rect.__init__(self, rect)
            self.label = label
            render1 = font1.render(label, 1, color1)
            if justify: self.centerx = r1.centerx
            self.render1 = Surface(render1.get_rect().inflate(3, 3).size, SRCALPHA)
            self.render1.blit(render1, (3, 3))
            self.render1.fill((0, 0, 0, shad), special_flags=BLEND_RGBA_MIN)
            # sub1 = scr.subsurface(self.move(3,3)).copy().convert_alpha()       # test uncomment
            # surfarray.pixels_alpha(sub1)[:] = surfarray.array_alpha(render1)   # test uncomment
            sub1 = render1  # test comment
            self.render1.blit(sub1, (0, 0))
            self.render2 = font2.render(label, 1, color2)
            self.white = self.render2.copy()
            self.white.fill((255, 255, 255, 0), special_flags=BLEND_RGBA_MAX)
            if neon:
                render2 = font2.render(label, 1, color1)
                renderneon = self.render2.copy()
                self.render2 = Surface(render2.get_rect().inflate(2, 2).size, SRCALPHA)
                for pos in ((0, 0), (0, 1), (0, 2), (1, 0), (2, 0), (0, 2), (1, 2), (2, 2)):
                    self.render2.blit(renderneon, pos)
                self.render2.blit(render2, (1, 1))
            if tooltip:
                tooltip = tooltipfont.render(tooltip, 1, (200, 200, 200))
                r = tooltip.get_rect().inflate(11, 7)
                self.tooltip = Surface(r.size, SRCALPHA)
                r = self.tooltip.fill((0, 0, 0, shad), (3, 3, r.w - 3, r.h - 3))
                r = self.tooltip.fill((200, 200, 200, 30), r.move(-3, -3))
                self.tooltip.fill((0, 0, 0, 200), r.inflate(-2, -2))
                self.tooltip.blit(tooltip, (4, 2))
            else:
                self.tooltip = None

    def show():
        i = Rect((0, 0), menu[idx].render2.get_size())
        if justify:
            i.center = menu[idx].center
        else:
            i.midleft = menu[idx].midleft
        del_cursor()
        scr.blit(bg, r2, r2)
        [scr.blit(item.render1, item) for item in menu if item != menu[idx]]
        scr.blit(menu[idx].white, i)
        show_cursor()
        display.update(r2)
        time.wait(50)
        del_cursor()
        r = scr.blit(menu[idx].render2, i)
        show_cursor()
        display.update(r.inflate(2, 2))
        return r

    def anim():
        a = [menu[0]] if lag else menu[:]
        c = 0
        qq = show_cursor()
        t1 = t2 = 0
        laps = 1000. / speed
        z = []
        while a:
            cc = clk.tick()
            t1 += cc
            t2 += cc

            if t2 >= 20 or t1 >= laps:
                dc = scr.blit(hold_bg_cursor, qq)
                z.append(dc)
                for ev in event.get():
                    if ev.type == MOUSEMOTION:
                        hold_rect_cursor.topleft = ev.pos
                        hold_rect_cursor.move_ip(-hotspot[0], -hotspot[1])
                t2 -= 20

            if t1 >= laps:
                for i in a:
                    z.append(scr.blit(bg, i.inflate(6, 6), i.inflate(6, 6)))
                    i.x = i.animx.pop(0)
                    z.append(scr.blit(i.render1, i).inflate(6, 6))
                c += 1
                if not a[0].animx:
                    i = a.pop(0)
                    if not lag: break
                if lag:
                    foo, bar = divmod(c, lag)
                    if not bar and foo < len(menu):
                        a.append(menu[foo])
                t1 -= laps

            if z:
                qq = show_cursor()
                z.append(qq)
                display.update(z)
                z = []

    def del_cursor():
        return scr.blit(hold_bg_cursor, scrrect.clip(hold_rect_cursor))

    def show_cursor():
        global hold_bg_cursor
        hold_bg_cursor = scr.subsurface(scrrect.clip(hold_rect_cursor)).copy()
        return scr.blit(cursor_img, hold_rect_cursor)

    was_visible = mouse.set_visible(not cursor_img)
    tooltip_offset = mouse.get_cursor()[0] if not cursor_img else (
    cursor_img.get_width() - hotspot[0], cursor_img.get_height() - hotspot[1])
    events = event.get()
    scr = display.get_surface()
    scrrect = scr.get_rect()
    bg = scr.copy()
    if not font1: font1 = font.Font(None, scrrect.h // len(menu) // 3)
    if not font2: font2 = font1
    if not color1: color1 = (128, 128, 128)
    if not color2: color2 = list(map(lambda x: x + int(((255 - x) if light > 0 else x) * (light / 10.)), color1))
    if not tooltipfont: tooltipfont = font.Font(None, int(font1.size('')[1] // 1.5))
    menu, tooltip = zip(*[i.partition('::')[0::2] for i in menu])
    m = max(menu, key=font1.size)
    r1 = Rect((0, 0), font1.size(m))
    ih = r1.size[1]
    r2 = Rect((0, 0), font2.size(m))
    r2.union_ip(r1)
    w, h = r2.w - r1.w, r2.h - r1.h
    r1.h = (r1.h + interline) * len(menu) - interline
    r2 = r1.inflate(w, h).inflate(6, 6)

    pos = {"x": x,
           "y": y,
           "topleft": topleft,
           "midtop": midtop,
           "topright": topright,
           "midleft": midleft,
           "center": center,
           "midright": midright,
           "bottomleft": bottomleft,
           "midbottom": midbottom,
           "bottomright": bottomright,
           "centerx": centerx,
           "centery": centery}

    r2.center = scrrect.center
    for k, v in pos.items():
        if v != None:
            setattr(r2, k, v)

    if justify:
        r1.center = r2.center
    else:
        r1.midleft = r2.midleft

    menu = [Item(((r1.x, r1.y + e * (ih + interline)), font1.size(i)), i, tooltip[e]) for e, i in enumerate(menu) if i]

    hold_rect_cursor = Rect(mouse.get_pos(), cursor_img.get_size() if cursor_img else (0, 0))
    hold_rect_cursor.move_ip(-hotspot[0], -hotspot[1])
    hold_bg_cursor = Surface((0, 0))
    if not cursor_img:
        cursor_img = Surface((0, 0))

    clk = time.Clock()
    clkt = 0
    if speed:
        for i in menu:
            z = r1.w - i.x + r1.x
            i.animx = [cos(radians(x)) * (i.x + z) - z for x in list(range(90, -1, -1))]
            i.x = i.animx.pop(0)
        anim()
        for i in menu:
            z = scrrect.w + i.x - r1.x
            i.animx = [cos(radians(x)) * (-z + i.x) + z for x in list(range(0, -91, -1))]
            i.x = i.animx.pop(0)

    # ~ display.update(del_cursor())

    # mouse.set_pos(menu[0].center)
    event.post(event.Event(MOUSEMOTION, {'pos': mouse.get_pos()}))
    idx = 0
    tooltip_seen = 0
    r = show()
    dirty = ()
    while True:
        ev = GetEvent.poll()
        if ev.type == NOEVENT and ev.inactiv >= tooltiptime:
            if not tooltip_seen and menu[idx].tooltip and r.collidepoint(mouse.get_pos()):
                rr0 = del_cursor()
                rcom = menu[idx].tooltip.get_rect(topleft=mouse.get_pos()).inflate(4, 4).move(tooltip_offset).clamp(
                    scrrect).clip(scrrect)
                combg = scr.subsurface(rcom).copy()
                scr.blit(menu[idx].tooltip, rcom)
                dirty += (rr0, rcom, show_cursor())
                tooltip_seen = 1
        if ev.type == MOUSEMOTION:
            idx_ = Rect(ev.pos, (0, 0)).collidelist(menu)
            if idx_ != idx:
                if tooltip_seen and (not r.collidepoint(mouse.get_pos()) or idx_ > -1):
                    dirty += (del_cursor(), scr.blit(combg, rcom), show_cursor())
                    tooltip_seen = 0
                if idx_ > -1:
                    idx = idx_
                    r = show()
            rr0 = del_cursor()
            hold_rect_cursor.topleft = ev.pos
            hold_rect_cursor.move_ip(-hotspot[0], -hotspot[1])
            dirty += (rr0, show_cursor())

        elif ev.type == MOUSEBUTTONUP and ev.button == 1 and r.collidepoint(ev.pos):
            ret = menu[idx].label, idx
            break
        elif ev.type == KEYDOWN:
            if ev.key == K_f:
                # Gestion des evenements claviers / Keyboard
                global MODE
                # bascule entre mode fenêtré et plein écran
                # switch display between windowed and full screen
                if MODE == 0:
                    MODE = FULLSCREEN
                else:
                    MODE = 0
                display.set_mode((640, 480), MODE)
                scr.blit(bg, bg.get_rect(center=scr.get_rect().center))
                display.flip()
                r = show()
            else:
                try:
                    idx = (idx + {K_UP: -1, K_DOWN: 1}[ev.key]) % len(menu)
                    if tooltip_seen:
                        dirty += (del_cursor(), scr.blit(combg, rcom), show_cursor())
                        tooltip_seen = 0
                    r = show()
                except:
                    if ev.key in (K_RETURN, K_KP_ENTER):
                        ret = menu[idx].label, idx
                        break
                    elif ev.key == K_ESCAPE:
                        ret = None, None
                        break
        clkt += clk.tick(500)
        if dirty:
            display.update(dirty)
            dirty = ()
            clkt = 0

    if tooltip_seen:
        display.update(scr.blit(combg, rcom))
    scr.blit(bg, r2, r2)

    if speed:
        [scr.blit(i.render1, i) for i in menu]
        display.update(r2)
        anim()
    else:
        display.update(r2)

    display.update(del_cursor())

    for ev in events: event.post(ev)
    mouse.set_visible(was_visible)
    return ret


if __name__ == '__main__':
    from os.path import dirname, join

    here = dirname(__file__)
    scr = display.set_mode((640, 480), MODE)
    bg = image.load(join(here, 'bg.png'))
    scr.blit(bg, bg.get_rect(center=scr.get_rect().center))
    # ~ scr.fill(-1)
    display.flip()
    print(menu.__doc__)

    while True:
        resp = menu(['one player',
                     'two players',
                     'level editor::not yet implemented',
                     '',
                     'options',
                     're-show::click here to show again',
                     'quit::good bye'],
                    font1=font.Font(join(here, '321impact.ttf'), 25),
                    font2=font.Font(join(here, '321impact.ttf'), 30),
                    tooltipfont=font.Font(join(here, "Roboto-MediumItalic.ttf"), 12),
                    color1=(255, 80, 40),
                    light=9,
                    tooltiptime=1000,
                    cursor_img=image.load('nepomuk.png'),
                    hotspot=(38, 15))

        if resp[0] != "re-show": break
    print(resp)
    quit()
